# -*- coding: utf-8 -*-

from hlm.node import TopModule
from hlm.task_manager import TaskManager


class Session:
    """ A session is the execution body of an architectural model. It invokes
        the top module and sets it as the root of async execution, where the
        top module will further invoke nodes and their children during each
        simulation step. In addition, session is the control-level for system
        activities including dump and traffic monitoring.
    """
    def __init__(self, top_module_type, shuffle: bool = False):
        # Create the task manager.
        self.__task_manager = TaskManager(shuffle=shuffle)
        # Set the top module type.
        self.__top_module = top_module_type()
        # Check top module type.
        if not isinstance(self.__top_module, TopModule):
            raise Exception('Only TopModule could be used in a session.')
        # Set the task list to task manager.
        self.__task_manager.set_task_list(self.__top_module.all_tasks())
        # Set session simulation step count
        self.__session_step = 0

    def session_step(self) -> int:
        """ Get the current session steps.
        Returns: The simulated step.
        """
        return self.__session_step

    def step(self) -> int:
        # Dummy for session step.
        return self.session_step()

    def top_module(self) -> TopModule:
        return self.__top_module

    def next_step(self) -> None:
        # Run the single step simulation.
        # Execute the task manager.
        self.__task_manager.run()
        # Increase the session step counter.
        self.__session_step += 1

    def n(self) -> None:
        # Dummy for next_step
        self.next_step()

    def next_n_step(self, n: int) -> None:
        # Execute the task manager.
        self.__task_manager.run_steps(n)
        # Increase the session step counter.
        self.__session_step += n

    def ns(self, n: int) -> None:
        # Dummy for next_n_step
        self.next_n_step(n)

    def nn(self, n: int) -> None:
        # Dummy for next_n_step
        self.next_n_step(n)